package utils

import (
	"MoSkeleton/framework/core/consts"
	"encoding/hex"
	"encoding/pem"
	"errors"
	"github.com/samber/lo"
	"github.com/tjfoc/gmsm/sm2"
	"github.com/tjfoc/gmsm/sm3"
	"github.com/tjfoc/gmsm/x509"
	"go.uber.org/zap"
	"os"
	"path/filepath"
)

var (
	sm2Privatekey *sm2.PrivateKey
)

/*
从前端传过来的密码，需先进行解密后再验证
*/
func PasswordValidateFromFront(password string, storedPasswd, salt string) bool {
	srcPasswd, err := DecryptPasswordFromFront(password)
	if err != nil {
		return false
	}
	return PasswordValidate(srcPasswd, storedPasswd, salt)
}

func DecryptPasswordFromFront(password string) (string, error) {
	switch SecurityPropertyInst.Password.PasswordTransferVer {
	case consts.Pwd_Transfer_Ver_Sm2:
		decodedBuf, _ := hex.DecodeString(password)
		decodedBuf = append([]byte{0x04}, decodedBuf...)
		srcPasswd, err := sm2.Decrypt(sm2Privatekey, decodedBuf, sm2.C1C3C2)
		return string(srcPasswd), err
	default:
		return password, nil
	}
}

/*
@password: 明文密码
*/
func PasswordValidate(password string, storedPasswd, salt string) bool {
	checkingPasswd := HashPasswd(password, salt)
	if checkingPasswd == storedPasswd {
		return true
	}
	return false
}

func HashPasswd(password string, salt string) string {
	internalPasswd := password + "-" + salt
	sm3Inst := sm3.New()
	sm3Inst.Write([]byte(internalPasswd))
	sum := sm3Inst.Sum(nil)
	return hex.EncodeToString(sum)
}

func GenPwdSalt() string {
	return lo.RandomString(15, lo.LettersCharset)
}

func initPasswordByConfig() {
	if SecurityPropertyInst.Password.PasswordTransferVer == consts.Pwd_Transfer_Ver_Sm2 {
		initSm2PrivateKey()
	}
}

func initSm2PrivateKey() {
	privateKeyFile := SecurityPropertyInst.Password.PrivateKeyFile
	if privateKeyFile == "" {
		privateKeyFile = consts.PwdPrivatekeyFile
	}
	prvFilename := filepath.Join(WorkDir, consts.PemPath, privateKeyFile)
	MoLogger.Debug("pem private key: ", zap.String("path", prvFilename))
	buffer, err := os.ReadFile(prvFilename)
	if err != nil {
		panic("load pem private key fail:" + prvFilename)
	}
	privateKey, err := readPrivateKeyFromPem(buffer, nil)
	if err != nil {
		panic("parse private key fail:" + prvFilename)
	}
	sm2Privatekey = privateKey
}

func readPrivateKeyFromPem(privateKeyPem []byte, pwd []byte) (*sm2.PrivateKey, error) {
	var block *pem.Block
	block, _ = pem.Decode(privateKeyPem)
	if block == nil {
		return nil, errors.New("failed to decode private key")
	}
	priv, err := x509.ParsePKCS8PrivateKey(block.Bytes, pwd)
	return priv, err
}
